Atraskite Python 'email' paketą. Išmokite kurti sudėtingus MIME pranešimus ir efektyviai analizuoti gaunamus el. laiškus duomenims išgauti globaliu mastu.
Python el. pašto paketo įvaldymas: MIME pranešimų kūrimo ir patikimo analizavimo menas
El. paštas išlieka kertiniu pasaulinės komunikacijos akmeniu, nepakeičiamu asmeniniam susirašinėjimui, verslo operacijoms ir automatizuotiems sistemos pranešimams. Už kiekvieno raiškiojo teksto el. laiško, kiekvieno priedo ir kiekvieno kruopščiai suformatuoto parašo slypi „Multipurpose Internet Mail Extensions“ (MIME) sudėtingumas. Programuotojams, ypač dirbantiems su Python, gebėjimas programiškai kurti ir analizuoti šiuos MIME pranešimus yra esminis įgūdis.
Python integruotas email
paketas suteikia tvirtą ir išsamią sistemą el. pašto pranešimams tvarkyti. Jis skirtas ne tik paprastų tekstinių laiškų siuntimui; jis sukurtas abstrahuoti sudėtingas MIME detales, leidžiant jums kurti įmantrius el. laiškus ir išgauti konkrečius duomenis iš gaunamų laiškų su nepaprastu tikslumu. Šis vadovas jus nuves į giluminę dviejų pagrindinių šio paketo aspektų apžvalgą: MIME pranešimų kūrimą siuntimui ir jų analizavimą duomenų išgavimui, pateikiant globalią geriausių praktikų perspektyvą.
Suprasti ir kūrimą, ir analizavimą yra labai svarbu. Kai kuriate pranešimą, iš esmės apibrėžiate jo struktūrą ir turinį, kad kita sistema galėtų jį interpretuoti. Kai analizuojate, interpretuojate kitos sistemos apibrėžtą struktūrą ir turinį. Gilus vieno aspekto supratimas labai padeda įvaldyti kitą, o tai leidžia kurti atsparesnes ir sąveikesnes el. pašto programas.
MIME supratimas: šiuolaikinio el. pašto pagrindas
Prieš gilinantis į Python specifiką, būtina suvokti, kas yra MIME ir kodėl jis toks svarbus. Iš pradžių el. pašto pranešimai buvo apriboti paprastu tekstu (7 bitų ASCII simboliais). MIME, pristatytas 10-ojo dešimtmečio pradžioje, išplėtė el. pašto galimybes, leisdamas palaikyti:
- Ne ASCII simboliai: Leidžia rašyti tekstą tokiomis kalbomis kaip arabų, kinų, rusų ar bet kuria kita kalba, kuri naudoja simbolius, nepriklausančius ASCII rinkiniui.
- Priedai: Siųsti failus, tokius kaip dokumentai, vaizdai, garso ir vaizdo įrašai.
- Raiškiojo teksto formatavimas: HTML el. laiškai su pastorinimu, kursyvu, spalvomis ir maketais.
- Kelios dalys: Sujungti paprastą tekstą, HTML ir priedus viename el. laiške.
MIME tai pasiekia pridėdamas specifines antraštes prie el. pašto pranešimo ir struktūrizuodamas jo turinį į įvairias „dalis“. Pagrindinės MIME antraštės, su kuriomis susidursite, yra:
Content-Type:
Nurodo duomenų tipą dalyje (pvz.,text/plain
,text/html
,image/jpeg
,application/pdf
,multipart/alternative
). Dažnai taip pat apimacharset
parametrą (pvz.,charset=utf-8
).Content-Transfer-Encoding:
Nurodo, kaip el. pašto klientas turėtų dekoduoti turinį (pvz.,base64
binariniams duomenims,quoted-printable
daugiausia tekstui su keliais ne ASCII simboliais).Content-Disposition:
Siūlo, kaip gavėjo el. pašto klientas turėtų rodyti dalį (pvz.,inline
rodymui pranešimo turinyje,attachment
kaip failą, kurį reikia išsaugoti).
Python email
paketas: išsami apžvalga
Python email
paketas yra išsami biblioteka, skirta programiškai kurti, analizuoti ir modifikuoti el. pašto pranešimus. Ji sukurta remiantis Message
objektų koncepcija, kurie atspindi el. laiško struktūrą.
Pagrindiniai paketo moduliai apima:
email.message:
Sudėtyje yra pagrindinėEmailMessage
klasė, kuri yra pirminė sąsaja el. pašto pranešimams kurti ir valdyti. Tai labai lanksti klasė, automatiškai tvarkanti MIME detales.email.mime:
Pateikia senesnes klases (pvz.,MIMEText
,MIMEMultipart
), kurios siūlo aiškesnę MIME struktūros kontrolę. Nors naujam kodui dėl paprastumo dažniausiai rekomenduojama naudotiEmailMessage
, šių klasių supratimas gali būti naudingas.email.parser:
Siūlo klases, tokias kaipBytesParser
irParser
, skirtas neapdorotiems el. pašto duomenims (baitams ar eilutėms) konvertuoti įEmailMessage
objektus.email.policy:
Apibrėžia politikas, kurios kontroliuoja, kaip el. pašto pranešimai yra kuriami ir analizuojami, veikiant antraščių kodavimą, eilučių pabaigas ir klaidų tvarkymą.
Daugumai šiuolaikinių naudojimo atvejų daugiausia sąveikausite su email.message.EmailMessage
klase tiek pranešimo kūrimui, tiek kaip analizuoto pranešimo objektu. Jos metodai gerokai supaprastina procesą, kuris anksčiau buvo žymiai sudėtingesnis su senesnėmis email.mime
klasėmis.
MIME pranešimų kūrimas: preciziškas el. laiškų konstravimas
El. laiškų kūrimas apima įvairių komponentų (teksto, HTML, priedų) surinkimą į galiojančią MIME struktūrą. EmailMessage
klasė šį procesą gerokai supaprastina.
Paprasti tekstiniai el. laiškai
Paprasčiausias el. laiškas yra paprastas tekstas. Jį galite sukurti ir be vargo nustatyti pagrindines antraštes:
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Sveikinimai iš Python'
msg['From'] = 'siuntejas@example.com'
msg['To'] = 'gavejas@example.com'
msg.set_content('Sveiki, tai yra paprasto teksto el. laiškas, išsiųstas iš Python.\n\nPagarbiai,\nJūsų Python scenarijus')
print(msg.as_string())
Paaiškinimas:
EmailMessage()
sukuria tuščią pranešimo objektą.- Į žodyną panaši prieiga (
msg['Subject'] = ...
) nustato įprastas antraštes. set_content()
prideda pagrindinį el. laiško turinį. Pagal nutylėjimą, ji nustatoContent-Type: text/plain; charset="utf-8"
.as_string()
serializuoja pranešimą į eilutės formatą, tinkamą siųsti per SMTP arba išsaugoti į failą.
HTML turinio pridėjimas
Norėdami išsiųsti HTML el. laišką, tiesiog nurodykite turinio tipą kviečiant set_content()
. Gera praktika yra pateikti paprasto teksto alternatyvą gavėjams, kurių el. pašto klientai neatvaizduoja HTML, arba dėl prieinamumo priežasčių.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Jūsų HTML naujienlaiškis'
msg['From'] = 'naujienlaiskis@example.com'
msg['To'] = 'prenumeratorius@example.com'
html_content = """
<html>
<head></head>
<body>
<h1>Sveiki atvykę į mūsų pasaulinę apžvalgą!</h1>
<p>Mielas prenumeratori,</p>
<p>Tai jūsų <strong>naujausia apžvalga</strong> iš viso pasaulio.</p>
<p>Apsilankykite mūsų <a href="http://www.example.com">svetainėje</a> norėdami sužinoti daugiau.</p>
<p>Pagarbiai,<br>Komanda</p>
</body>
</html>
"""
# Pridedame HTML versiją
msg.add_alternative(html_content, subtype='html')
# Pridedame paprasto teksto atsarginę versiją
plain_text_content = (
"Sveiki atvykę į mūsų pasaulinę apžvalgą!\n\n"
"Mielas prenumeratori,\n\n"
"Tai jūsų naujausia apžvalga iš viso pasaulio.\n"
"Apsilankykite mūsų svetainėje norėdami sužinoti daugiau: http://www.example.com\n\n"
"Pagarbiai,\nKomanda"
)
msg.add_alternative(plain_text_content, subtype='plain')
print(msg.as_string())
Paaiškinimas:
add_alternative()
naudojama pridėti skirtingus *to paties* turinio atvaizdus. El. pašto klientas parodys „geriausią“, kurį gali apdoroti (dažniausiai HTML).- Tai automatiškai sukuria
multipart/alternative
MIME struktūrą.
Darbas su priedais
Failų pridėjimas yra paprastas naudojant add_attachment()
. Galite pridėti bet kokio tipo failą, o paketas pasirūpins atitinkamais MIME tipais ir kodavimu (dažniausiai base64
).
from email.message import EmailMessage
from pathlib import Path
# Sukuriame demonstracinius failus
Path('report.pdf').write_bytes(b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Count 0>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n104\n%%EOF') # Labai paprastas, negaliojantis PDF pavyzdys
Path('logo.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82') # 1x1 permatomas PNG pavyzdys
msg = EmailMessage()
msg['Subject'] = 'Svarbus dokumentas ir paveikslėlis'
msg['From'] = 'siuntejas@example.com'
msg['To'] = 'gavejas@example.com'
msg.set_content('Prašome rasti pridėtą ataskaitą ir įmonės logotipą.')
# Pridedame PDF failą
with open('report.pdf', 'rb') as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype='application',
subtype='pdf',
filename='Metine_Ataskaita_2024.pdf'
)
# Pridedame paveikslėlio failą
with open('logo.png', 'rb') as f:
image_data = f.read()
msg.add_attachment(
image_data,
maintype='image',
subtype='png',
filename='ImonesLogotipas.png'
)
print(msg.as_string())
# Išvalome demonstracinius failus
Path('report.pdf').unlink()
Path('logo.png').unlink()
Paaiškinimas:
add_attachment()
priima neapdorotus failo turinio baitus.maintype
irsubtype
nurodo MIME tipą (pvz.,application/pdf
,image/png
). Tai yra labai svarbu, kad gavėjo el. pašto klientas teisingai atpažintų ir apdorotų priedą.filename
nurodo pavadinimą, kuriuo priedas bus išsaugotas gavėjo sistemoje.- Tai automatiškai sukuria
multipart/mixed
struktūrą.
Kelių dalių (multipart) pranešimų kūrimas
Kai turite pranešimą su HTML turiniu, paprasto teksto atsargine versija ir įterptaisiais paveikslėliais ar susijusiais failais, jums reikia sudėtingesnės kelių dalių struktūros. EmailMessage
klasė protingai tai tvarko su add_related()
ir add_alternative()
.
Dažnas scenarijus yra HTML el. laiškas su paveikslėliu, įterptu tiesiai į HTML (įterptasis arba „inline“ paveikslėlis). Tam naudojama multipart/related
.
from email.message import EmailMessage
from pathlib import Path
# Sukuriame demonstracinį paveikslėlio failą (1x1 permatomas PNG)
Path('banner.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82')
msg = EmailMessage()
msg['Subject'] = 'Įterptojo paveikslėlio pavyzdys'
msg['From'] = 'siuntejas@example.com'
msg['To'] = 'gavejas@example.com'
# Paprasto teksto versija (atsarginė)
plain_text = 'Peržiūrėkite mūsų nuostabų banerį!\n\n[Paveikslėlis: Banner.png]\n\nApsilankykite mūsų svetainėje.'
msg.set_content(plain_text, subtype='plain') # Nustatome pradinį paprasto teksto turinį
# HTML versija (su CID įterptajam paveikslėliui)
html_content = """
<html>
<head></head>
<body>
<h1>Mūsų naujausias pasiūlymas!</h1>
<p>Mielas kliente,</p>
<p>Nepraleiskite mūsų specialios pasaulinės akcijos:</p>
<img src="cid:my-banner-image" alt="Akcijos baneris">
<p>Spauskite <a href="http://www.example.com">čia</a>, kad sužinotumėte daugiau.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html') # Pridedame HTML alternatyvą
# Pridedame įterptąjį paveikslėlį (susijusį turinį)
with open('banner.png', 'rb') as img_file:
image_data = img_file.read()
msg.add_related(
image_data,
maintype='image',
subtype='png',
cid='my-banner-image' # Šis CID atitinka 'src' atributą HTML
)
print(msg.as_string())
# Išvalome demonstracinį failą
Path('banner.png').unlink()
Paaiškinimas:
set_content()
nustato pradinį turinį (šiuo atveju, paprastą tekstą).add_alternative()
prideda HTML versiją, sukurdamamultipart/alternative
struktūrą, kurioje yra paprasto teksto ir HTML dalys.add_related()
naudojama turiniui, kuris yra „susijęs“ su viena iš pranešimo dalių, dažniausiai įterptaisiais paveikslėliais HTML. Ji priimacid
(Content-ID) parametrą, kuris vėliau nurodomas HTML<img src="cid:my-banner-image">
žymoje.- Galutinė struktūra bus
multipart/mixed
(jei būtų išorinių priedų), kurioje yramultipart/alternative
dalis, o ši savo ruožtu turimultipart/related
dalį.multipart/related
dalyje yra HTML ir įterptasis paveikslėlis.EmailMessage
klasė pasirūpina šiuo sudėtingu įdėjimu už jus.
Kodavimas ir simbolių rinkiniai globaliam pasiekiamumui
Tarptautinei komunikacijai tinkamas simbolių kodavimas yra labai svarbus. email
paketas pagal nutylėjimą yra labai orientuotas į UTF-8 naudojimą, kuris yra universalus standartas, skirtas tvarkyti įvairius simbolių rinkinius iš viso pasaulio.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Globalūs simboliai: こんにちは, Привет, नमस्ते'
msg['From'] = 'globalus_siuntejas@example.com'
msg['To'] = 'globalus_gavejas@example.com'
# Japoniški, rusiški ir hindi simboliai
content = "Šiame pranešime yra įvairių globalių simbolių:\n"
content += "こんにちは (japonų k.)\n"
content += "Привет (rusų k.)\n"
content += "नमस्ते (hindi k.)\n\n"
content += "'email' paketas puikiai tvarko UTF-8."
msg.set_content(content)
print(msg.as_string())
Paaiškinimas:
- Kai
set_content()
gauna Python eilutę, ji automatiškai ją užkoduoja į UTF-8 ir nustatoContent-Type: text/plain; charset="utf-8"
antraštę. - Jei turinys to reikalauja (pvz., jame yra daug ne ASCII simbolių), ji taip pat gali pritaikyti
Content-Transfer-Encoding: quoted-printable
arbabase64
, kad užtikrintų saugų perdavimą per senesnes el. pašto sistemas. Paketas tai tvarko automatiškai pagal pasirinktą politiką.
Pasirinktinės antraštės ir politikos
Galite pridėti bet kokią pasirinktinę antraštę prie el. laiško. Politikos (iš email.policy
) apibrėžia, kaip pranešimai yra tvarkomi, paveikdamos tokius aspektus kaip antraščių kodavimas, eilučių pabaigos ir klaidų tvarkymas. Numatytoji politika paprastai yra gera, bet galite pasirinkti `SMTP` griežtam SMTP atitikimui arba apibrėžti pasirinktines.
from email.message import EmailMessage
from email import policy
msg = EmailMessage(policy=policy.SMTP)
msg['Subject'] = 'El. laiškas su pasirinktine antrašte'
msg['From'] = 'info@example.org'
msg['To'] = 'user@example.org'
msg['X-Custom-Header'] = 'Tai yra pasirinktinė reikšmė sekimui'
msg['Reply-To'] = 'support@example.org'
msg.set_content('Šis el. laiškas demonstruoja pasirinktines antraštes ir politikas.')
print(msg.as_string())
Paaiškinimas:
- Naudojant
policy=policy.SMTP
užtikrinamas griežtas atitikimas SMTP standartams, kas gali būti labai svarbu pristatomumui. - Pasirinktinės antraštės pridedamos taip pat, kaip ir standartinės. Jos dažnai prasideda
X-
, kad nurodytų nestandartines antraštes.
MIME pranešimų analizavimas: informacijos išgavimas iš gaunamų el. laiškų
Analizavimas apima neapdorotų el. pašto duomenų (dažniausiai gautų per IMAP arba iš failo) paėmimą ir jų konvertavimą į `EmailMessage` objektą, kurį vėliau galite lengvai tikrinti ir valdyti.
Įkėlimas ir pirminis analizavimas
Paprastai el. laiškus gausite kaip neapdorotus baitus. Tam naudojamas email.parser.BytesParser
(arba patogumo funkcijos email.message_from_bytes()
).
from email.parser import BytesParser
from email.policy import default
raw_email_bytes = b"""
From: siuntejas@example.com
To: gavejas@example.com
Subject: Testinis el. laiškas su pagrindinėmis antraštėmis
Date: Mon, 1 Jan 2024 10:00:00 +0000
Content-Type: text/plain; charset="utf-8"
Tai yra el. laiško turinys.
Tai paprastas testas.
"""
# Naudojant BytesParser
parser = BytesParser(policy=default)
msg = parser.parsebytes(raw_email_bytes)
# Arba naudojant patogumo funkciją
# from email import message_from_bytes
# msg = message_from_bytes(raw_email_bytes, policy=default)
print(f"Tema: {msg['subject']}")
print(f"Nuo: {msg['from']}")
print(f"Content-Type: {msg['Content-Type']}")
Paaiškinimas:
BytesParser
paima neapdorotus baitų duomenis (taip el. laiškai yra perduodami) ir grąžinaEmailMessage
objektą.policy=default
nurodo analizavimo taisykles.
Prieiga prie antraščių
Antraštės yra lengvai pasiekiamos per į žodyną panašius raktus. Paketas automatiškai tvarko užkoduotų antraščių dekodavimą (pvz., temas su tarptautiniais simboliais).
# ... (naudojant 'msg' objektą iš ankstesnio analizavimo pavyzdžio)
print(f"Data: {msg['date']}")
print(f"Pranešimo ID: {msg['Message-ID'] if 'Message-ID' in msg else 'N/A'}")
# Tvarkant kelias antraštes (pvz., 'Received' antraštes)
# from email.message import EmailMessage # Jei dar neimportuota
# from email import message_from_string # Greitam eilutės pavyzdžiui
multi_header_email = message_from_string(
"""
From: a@example.com
To: b@example.com
Subject: Kelių antraščių testas
Received: from client.example.com (client.example.com [192.168.1.100])
by server.example.com (Postfix) with ESMTP id 123456789
for <b@example.com>; Mon, 1 Jan 2024 10:00:00 +0000 (GMT)
Received: from mx.another.com (mx.another.com [192.168.1.101])
by server.example.com (Postfix) with ESMTP id 987654321
for <b@example.com>; Mon, 1 Jan 2024 09:59:00 +0000 (GMT)
Turinys čia.
"""
)
received_headers = multi_header_email.get_all('received')
if received_headers:
print("\nGautos antraštės (Received Headers):")
for header in received_headers:
print(f"- {header}")
Paaiškinimas:
- Prieiga prie antraštės grąžina jos reikšmę kaip eilutę.
get_all('header-name')
yra naudinga antraštėms, kurios gali pasikartoti kelis kartus (pvz.,Received
).- Paketas tvarko antraščių dekodavimą, todėl reikšmės kaip
Subject: =?utf-8?Q?Global_Characters:_=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
yra automatiškai konvertuojamos į skaitomas eilutes.
Pagrindinio turinio išgavimas
Norint išgauti tikrąjį pranešimo turinį, reikia patikrinti, ar pranešimas yra kelių dalių. Jei pranešimas yra kelių dalių, reikia iteruoti per jo dalis.
from email.message import EmailMessage
from email import message_from_string
multipart_email_raw = """
From: multi@example.com
To: user@example.com
Subject: Testinis kelių dalių el. laiškas
Content-Type: multipart/alternative; boundary="_----------=_12345"
--_----------=_12345
Content-Type: text/plain; charset="utf-8"
Sveiki iš paprasto teksto dalies!
--_----------=_12345
Content-Type: text/html; charset="utf-8"
<html>
<body>
<h1>Sveiki iš HTML dalies!</h1>
<p>Tai yra <strong>raiškiojo teksto</strong> el. laiškas.</p>
</body>
</html>
--_----------=_12345--
"""
msg = message_from_string(multipart_email_raw)
if msg.is_multipart():
print("\n--- Kelių dalių el. laiško turinys ---")
for part in msg.iter_parts():
content_type = part.get_content_type()
charset = part.get_content_charset() or 'utf-8' # Jei nenurodyta, naudojame utf-8
payload = part.get_payload(decode=True) # Dekoduojame turinio baitus
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {content_type}, Simbolių rinkinys: {charset}\nTurinys:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content-Type: {content_type}, Simbolių rinkinys: {charset}\nTurinys: (Binariniai arba nedekoduojami duomenys)\n")
# Tvarkome binarinius duomenis arba bandome atsarginį kodavimą
else:
print("\n--- Vienos dalies el. laiško turinys ---")
charset = msg.get_content_charset() or 'utf-8'
payload = msg.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {msg.get_content_type()}, Simbolių rinkinys: {charset}\nTurinys:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Turinys: (Binariniai arba nedekoduojami duomenys)\n")
Paaiškinimas:
is_multipart()
nustato, ar el. laiškas turi kelias dalis.iter_parts()
iteruoja per visas kelių dalių pranešimo dalis.get_content_type()
grąžina pilną MIME tipą (pvz.,text/plain
).get_content_charset()
išgauna simbolių rinkinį (charset) išContent-Type
antraštės.get_payload(decode=True)
yra labai svarbus: jis grąžina *dekoduotą* turinį kaip baitus. Tada šiuos baitus reikia.decode()
naudojant teisingą simbolių rinkinį, kad gautumėte Python eilutę.
Darbas su priedais analizavimo metu
Priedai taip pat yra kelių dalių pranešimo dalys. Juos galima identifikuoti pagal Content-Disposition
antraštę ir išsaugoti jų dekoduotą turinį.
from email.message import EmailMessage
from email import message_from_string
import os
# Pavyzdinis el. laiškas su paprastu priedu
email_with_attachment = """
From: attach@example.com
To: user@example.com
Subject: Pridėtas dokumentas
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_----------=_XYZ"
--_----------=_XYZ
Content-Type: text/plain; charset="utf-8"
Čia yra jūsų prašytas dokumentas.
--_----------=_XYZ
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"
JVBERi0xLjQKMSAwIG9iagpbL1BERi9UZXh0L0ltYWdlQy9JbWFnZUkvSW1hZ0VCXQplbmRvYmoK
--_----------=_XYZ--
"""
msg = message_from_string(email_with_attachment)
output_dir = 'analizuoti_priedai'
os.makedirs(output_dir, exist_ok=True)
print("\n--- Apdorojami priedai ---")
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Išsaugotas priedas: {filepath} (Tipas: {part.get_content_type()})")
except Exception as e:
print(f"Klaida išsaugant {filename}: {e}")
else:
print(f"Rastas priedas be failo pavadinimo (Content-Type: {part.get_content_type()})")
# Išvalome išvesties katalogą
# import shutil
# shutil.rmtree(output_dir)
Paaiškinimas:
iter_attachments()
specialiai grąžina dalis, kurios tikėtinai yra priedai (t.y., turiContent-Disposition: attachment
antraštę arba nėra kitaip klasifikuotos).get_filename()
išgauna failo pavadinimą išContent-Disposition
antraštės.part.get_payload(decode=True)
gauna neapdorotą binarinį priedo turinį, jau dekoduotą išbase64
arquoted-printable
.
Koduočių ir simbolių rinkinių dekodavimas
email
paketas puikiai atlieka automatinį įprastų perdavimo koduočių (pvz., base64
, quoted-printable
) dekodavimą, kai kviečiate get_payload(decode=True)
. Pačiam tekstiniam turiniui jis bando naudoti charset
, nurodytą Content-Type
antraštėje. Jei simbolių rinkinys nenurodytas arba yra neteisingas, gali tekti tai tvarkyti atskirai.
from email.message import EmailMessage
from email import message_from_string
# Pavyzdys su potencialiai problemišku simbolių rinkiniu
email_latin1 = """
From: legacy@example.com
To: new_system@example.com
Subject: Specialūs simboliai: àéíóú
Content-Type: text/plain; charset="iso-8859-1"
Šiame pranešime yra Latin-1 simbolių: àéíóú
"""
msg = message_from_string(email_latin1)
if msg.is_multipart():
for part in msg.iter_parts():
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
print(f"Dekoduota (Simbolių rinkinys: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Nepavyko dekoduoti su {charset}. Bandoma atsarginė parinktis...")
# Atsarginė parinktis - dažnas simbolių rinkinys arba 'latin-1', jei tikimasi
print(f"Dekoduota (Atsarginis Latin-1): {payload.decode('latin-1', errors='replace')}")
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
try:
print(f"Dekoduota (Simbolių rinkinys: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Nepavyko dekoduoti su {charset}. Bandoma atsarginė parinktis...")
print(f"Dekoduota (Atsarginis Latin-1): {payload.decode('latin-1', errors='replace')}")
Paaiškinimas:
- Visada stenkitės naudoti simbolių rinkinį, nurodytą
Content-Type
antraštėje. - Naudokite
try-except UnicodeDecodeError
bloką patikimumui užtikrinti, ypač dirbant su el. laiškais iš įvairių ir potencialiai nestandartinių šaltinių. errors='replace'
arbaerrors='ignore'
gali būti naudojami su.decode()
, kad būtų tvarkomi simboliai, kurių negalima atvaizduoti tikslinėje koduotėje, taip išvengiant programos strigimo.
Pažangūs analizavimo scenarijai
Realaus pasaulio el. laiškai gali būti labai sudėtingi, su įdėtomis kelių dalių struktūromis. email
paketo rekursinė prigimtis leidžia lengvai naršyti po jas. Galite derinti is_multipart()
su iter_parts()
, kad pereitumėte per giliai įdėtus pranešimus.
from email.message import EmailMessage
from email import message_from_string
def parse_email_part(part, indent=0):
prefix = " " * indent
content_type = part.get_content_type()
charset = part.get_content_charset() or 'N/A'
print(f"{prefix}Dalies tipas: {content_type}, Simbolių rinkinys: {charset}")
if part.is_multipart():
for subpart in part.iter_parts():
parse_email_part(subpart, indent + 1)
elif part.get_filename(): # Tai priedas
print(f"{prefix} Priedas: {part.get_filename()} (Dydis: {len(part.get_payload(decode=True))} baitų)")
else: # Tai įprasta text/html turinio dalis
payload = part.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
# print(f"{prefix} Turinys (pirmieji 100 simbolių): {decoded_content[:100]}...") # Trumpumo dėlei
except UnicodeDecodeError:
print(f"{prefix} Turinys: (Binarinis arba nedekoduojamas tekstas)")
complex_email_raw = """
From: complex@example.com
To: receiver@example.com
Subject: Sudėtingas el. laiškas su HTML, paprastu tekstu ir priedu
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary"
--outer_boundary
Content-Type: multipart/alternative; boundary="inner_boundary"
--inner_boundary
Content-Type: text/plain; charset="utf-8"
Paprasto teksto turinys.
--inner_boundary
Content-Type: text/html; charset="utf-8"
<html><body><h2>HTML turinys</h2></body></html>
--inner_boundary--
--outer_boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="data.bin"
SGVsbG8gV29ybGQh
--outer_boundary--
"""
msg = message_from_string(complex_email_raw)
print("\n--- Einama per sudėtingo el. laiško struktūrą ---")
parse_email_part(msg)
Paaiškinimas:
- Rekursinė funkcija
parse_email_part
parodo, kaip pereiti per visą pranešimo medį, identifikuojant kelių dalių dalis, priedus ir turinį kiekviename lygmenyje. - Šis modelis yra labai lankstus, norint išgauti specifinių tipų turinį iš giliai įdėtų el. laiškų.
Kūrimas ir analizavimas: lyginamoji perspektyva
Nors tai yra skirtingos operacijos, kūrimas ir analizavimas yra dvi tos pačios monetos pusės: MIME pranešimų tvarkymas. Vieno supratimas neišvengiamai padeda kitam.
Kūrimas (siuntimas):
- Fokusas: Teisingas antraščių, turinio ir priedų surinkimas į standartus atitinkančią MIME struktūrą.
- Pagrindinis įrankis:
email.message.EmailMessage
su metodais, tokiais kaipset_content()
,add_attachment()
,add_alternative()
,add_related()
. - Pagrindiniai iššūkiai: Užtikrinti teisingus MIME tipus, simbolių rinkinius (ypač UTF-8 globaliam palaikymui), `Content-Transfer-Encoding` ir tinkamą antraščių formatavimą. Klaidos gali lemti, kad el. laiškai nebus rodomi teisingai, priedai bus sugadinti arba pranešimai bus pažymėti kaip šlamštas.
Analizavimas (gavimas):
- Fokusas: Neapdoroto el. laiško baitų srauto išardymas į jo sudedamąsias dalis, išgaunant specifines antraštes, turinį ir priedus.
- Pagrindinis įrankis:
email.parser.BytesParser
arbaemail.message_from_bytes()
, tada naršymas po gautąEmailMessage
objektą su metodais, tokiais kaipis_multipart()
,iter_parts()
,get_payload()
,get_filename()
ir prieiga prie antraščių. - Pagrindiniai iššūkiai: Tvarkyti neteisingai suformuotus el. laiškus, teisingai identifikuoti simbolių kodavimą (ypač kai jis dviprasmiškas), susidoroti su trūkstamomis antraštėmis ir patikimai išgauti duomenis iš įvairių MIME struktūrų.
Pranešimas, kurį sukuriate naudodami `EmailMessage`, turėtų būti puikiai analizuojamas `BytesParser`. Panašiai, supratimas apie MIME struktūrą, gautą analizavimo metu, suteikia įžvalgų, kaip patiems kurti sudėtingus pranešimus.
Geriausios praktikos dirbant su globaliu el. paštu naudojant Python
Programoms, kurios sąveikauja su globalia auditorija arba tvarko įvairius el. pašto šaltinius, apsvarstykite šias geriausias praktikas:
- Standartizuokite UTF-8: Visada naudokite UTF-8 visam tekstiniam turiniui, tiek kuriant, tiek tikintis jo analizavimo metu. Tai yra globalus simbolių kodavimo standartas ir padeda išvengti „mojibake“ (sugadinto teksto).
- Patikrinkite el. pašto adresus: Prieš siunčiant, patikrinkite gavėjų el. pašto adresus, kad užtikrintumėte pristatomumą. Analizuojant, būkite pasirengę potencialiai neteisingiems ar netinkamai suformuotiems adresams `From`, `To` ar `Cc` antraštėse.
- Kruopščiai testuokite: Testuokite savo el. laiškų kūrimą su įvairiais el. pašto klientais („Gmail“, „Outlook“, „Apple Mail“, „Thunderbird“) ir platformomis, kad užtikrintumėte nuoseklų HTML ir priedų atvaizdavimą. Analizavimui, testuokite su plačiu pavyzdinių el. laiškų spektru, įskaitant tuos su neįprastais kodavimais, trūkstamomis antraštėmis ar sudėtingomis įdėtomis struktūromis.
- Dezinfekuokite analizuotą įvestį: Visada traktuokite turinį, išgautą iš gaunamų el. laiškų, kaip nepatikimą. Dezinfekuokite HTML turinį, kad išvengtumėte XSS atakų, jei jį rodote interneto programoje. Patikrinkite priedų failų pavadinimus ir tipus, kad išvengtumėte kelio apėjimo ar kitų saugumo pažeidžiamumų saugant failus.
- Patikimas klaidų tvarkymas: Įdiekite išsamius
try-except
blokus, kai dekoduojate turinį ar prieinate prie potencialiai trūkstamų antraščių. Tvarkingai apdorokiteUnicodeDecodeError
arKeyError
klaidas. - Tvarkykite didelius priedus: Atsižvelkite į priedų dydžius, tiek kuriant (kad neviršytumėte pašto serverio apribojimų), tiek analizuojant (kad išvengtumėte per didelio atminties ar disko vietos naudojimo). Apsvarstykite didelių priedų srautinį perdavimą, jei jūsų sistema tai palaiko.
- Naudokite
email.policy
: Kritinėms programoms aiškiai pasirinkite `email.policy` (pvz., `policy.SMTP`), kad užtikrintumėte griežtą atitiktį el. pašto standartams, kas gali paveikti pristatomumą ir sąveikumą. - Metaduomenų išsaugojimas: Analizuojant, nuspręskite, kokie metaduomenys (antraštės, originalios ribų eilutės) yra svarbūs išsaugoti, ypač jei kuriate pašto archyvavimo ar persiuntimo sistemą.
Išvada
Python email
paketas yra neįtikėtinai galinga ir lanksti biblioteka kiekvienam, kuriam reikia programiškai sąveikauti su el. paštu. Įvaldę tiek MIME pranešimų kūrimą, tiek patikimą gaunamų el. laiškų analizavimą, atveriate galimybę kurti sudėtingas el. pašto automatizavimo sistemas, kurti el. pašto klientus, analizuoti el. pašto duomenis ir integruoti el. pašto funkcijas į beveik bet kurią programą.
Paketas apgalvotai tvarko sudėtingas MIME detales, leisdamas programuotojams susitelkti į savo el. pašto sąveikų turinį ir logiką. Nesvarbu, ar siunčiate asmeninius naujienlaiškius globaliai auditorijai, ar išgaunate kritinius duomenis iš automatizuotų sistemos ataskaitų, gilus email
paketo supratimas pasirodys neįkainojamas kuriant patikimus, sąveikius ir globaliai orientuotus el. pašto sprendimus.